﻿using SharpSoft.Lexing;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SharpSoft.Documentation.Html.CSS
{
    /// <summary>
    /// Css值词法分析程序
    /// </summary>
    public class CssValueLexer : _TextLexer
    {
        #region ctor

        public CssValueLexer(string source)
            : base(source)
        {

        }

        public CssValueLexer(TextSource ts)
            : base(ts)
        {

        }
        #endregion

        #region general

        /// <summary>
        /// 读取Css长度
        /// </summary>
        /// <param name="isfont">设置该长度是否是用来描述字体大小</param>
        /// <returns></returns>
        public CssLength? ReadCssLength(bool isfont)
        {
            SkipWhiteSpace();
            char c = Current;
            char p = Peek();
            float value = 0;
            float factor = 1f;
            CssLengthUnit unit = CssLengthUnit.Px;
            if (c.IsDigit())
            {
                var str = ReadNumeric();
                value = float.Parse(str);
            }
            else
            {
                return null;//不能读取长度
            }
            c = Current;
            p = Peek();
            string u = string.Concat(c, p).ToLower();
            if (EOS() || c.IsWhiteSpace())
            {
                return new CssLength() { Value = value * factor, Unit = unit };//默认为像素
            }
            if (c == '%')
            {
                unit = CssLengthUnit.Percent;
                value = 0.01f;
                c = Next();
                if (!EOS() || !c.IsWhiteSpace())
                    throw new CssSyntaxException(Position, 1, "无效的字符：" + c);
            }
            else
            {
                switch (u)
                {
                    case CssConstants.Px:
                        unit = CssLengthUnit.Px;
                        factor = isfont ? 72f / 96f : factor;
                        break;
                    case CssConstants.Em:
                        unit = CssLengthUnit.Em;
                        factor = 1f;
                        break;
                    case CssConstants.Mm:
                        unit = CssLengthUnit.Mm;
                        factor = 3f;
                        break;
                    case CssConstants.Cm:
                        unit = CssLengthUnit.Cm;
                        factor = 37f;
                        break;
                    case CssConstants.In:
                        unit = CssLengthUnit.In;
                        factor = 96f;
                        break;
                    case CssConstants.Pt:
                        unit = CssLengthUnit.Pt;
                        factor = 96f / 72f;
                        break;
                    case CssConstants.Pc:
                        unit = CssLengthUnit.Pc;
                        factor = 96f / 72f * 12f;
                        break;
                    case CssConstants.Ex:
                        unit = CssLengthUnit.Ex;
                        factor = 0.5f;
                        break;
                    default:
                        throw new CssSyntaxException(Position, 2, "不可识别的长度单位：" + u);
                }
                c = Next(2);
                if (!EOS() && !c.IsWhiteSpace())
                    throw new CssSyntaxException(Position, 1, "无效的字符：" + c);
            }
            return new CssLength() { Value = value * factor, Unit = unit };
        }

        /// <summary>
        /// 读取外边距
        /// </summary>
        /// <returns>返回描述四边外边距的数据</returns>
        public CssLength[] ReadCssLengths()
        {
            SkipWhiteSpace();
            List<CssLength> list = new List<CssLength>(4);
            while (!EOS())
            {
                var l = ReadCssLength(false);
                if (l.HasValue)
                    list.Add(l.Value);
                else
                {
                    break;
                    //throw new CssSyntaxException(Position,1,"无法解析外边距设置。");
                }
            }
            if (list.Count == 1)//只有一个数据
            {
                for (int i = 0; i < 3; i++)
                {
                    list.Add(list[0]);
                }
            }
            return list.ToArray();
        }

        /// <summary>
        /// 读取Css的颜色值
        /// </summary>
        /// <returns>返回一个描述颜色值的整型</returns>
        public int? ReadCssColor()
        {
            SkipWhiteSpace();
            char c = Current;
            char p = Peek();
            if (c == '#')//形如： #EED1FF
            {
                StringBuilder sb = new StringBuilder(10);
                while (!EOS())
                {
                    if (c.IsHex())
                        sb.Append(c);
                    else if (c.IsWhiteSpace())
                        break;
                    else
                    {
                        throw new CssSyntaxException(Position, 1, "[" + c + "]字符对于描述颜色值无效。");
                    }
                    c = Next();
                }
                string str = sb.ToString();
                if (str.Length == 0)
                    throw new CssSyntaxException(Position, 1, "无效的颜色值描述。");
                return Convert.ToInt32("0x" + str, 16);
            }
            else
            {
                string colorname = ReadIdentifier().ToLower();//读取一个标识符尝试做为颜色名称的描述
                if (colorname == "rgb")//处理函数形式的rgb(r,g,b)颜色值描述
                {
                    throw new Exception("暂不支持rgb(r,g,b)形式的颜色描述");
                }
                else if (colorname == "rgba")//处理函数形式的rgba(r,g,b,a)颜色值描述
                {
                    throw new Exception("暂不支持rgba(r,g,b,a)形式的颜色描述");
                }
                else if (colorname == "hsl")
                {
                    throw new Exception("暂不支持hsl(h,s,l)形式的颜色描述");
                }
                else if (colorname == "hsla")
                {
                    throw new Exception("暂不支持hsla(h,s,l,a)形式的颜色描述");
                }
                else
                    return Utility.GetColorFromWebColorName(colorname);
            } 
        }
        /// <summary>
        /// 读取一组描述颜色的值
        /// </summary>
        /// <returns></returns>
        public int[] ReadCssColors()
        {
            SkipWhiteSpace();
            List<int> list = new List<int>(4);
            while (!EOS())
            {
                var c = ReadCssColor();
                if (c.HasValue)
                    list.Add(c.Value);
                else
                {
                    break; 
                }
            }
            if (list.Count == 1)//只有一个数据
            {
                for (int i = 0; i < 3; i++)
                {
                    list.Add(list[0]);
                }
            }
            return list.ToArray();
        }
        #endregion

        #region Border

        /// <summary>
        /// 读取边框样式
        /// </summary>
        /// <returns></returns>
        private CssBorderStyle? readBorderStyle()
        {
            SkipWhiteSpace();
            string str = ReadIdentifier();
            if (string.IsNullOrEmpty(str))
                return null;
            str = str.ToLower();
            switch (str)
            {
                case "dashed":
                    return CssBorderStyle.dashed;
                case "dotted":
                    return CssBorderStyle.dotted;
                case "double":
                    return CssBorderStyle.@double;
                case "groove":
                    return CssBorderStyle.groove;
                case "hidden":
                    return CssBorderStyle.hidden;
                case "none":
                    return CssBorderStyle.none;
                case "ridge":
                    return CssBorderStyle.ridge;
                case "solid":
                    return CssBorderStyle.solid;
                default:
                    return null;  //throw new CssSyntaxException(Position, 1, str + " 无法解析为边框样式");
            }
        }
        /// <summary>
        /// 读取描述4边的边框样式
        /// </summary>
        /// <returns></returns>
        public CssBorderStyle[] ReadBorderStyles()
        {
            List<CssBorderStyle> l = new List<CssBorderStyle>(4);
            while (!EOS())
            {
                var style = readBorderStyle();
                if (style.HasValue)
                    l.Add(style.Value);
                if (EOS())
                    break;
            }
            if (l.Count==1)
            {
                for (int i = 0; i < 3; i++)
                    l.Add(l[0]);
            }
            return l.ToArray();
        }
        /// <summary>
        /// 读取边框
        /// </summary>
        /// <returns></returns>
        public CssBorder ReadBorder()
        {
            CssBorderStyle? style = null;
            CssLength? width = null;
            int? color = null;
            while (!EOS())
            {
                int pos = Position;
                int all = 0;
                var style1 = readBorderStyle();
                if (style1 == null)
                {
                    Next(pos - Position);//返回读前的位置
                    all++;
                }
                else
                {
                    style = style1;
                    continue;
                }
                var width1 = ReadCssLength(false);
                if (width1 == null)
                {
                    all++;
                    Next(pos - Position);//返回读前的位置
                }
                else
                {
                    width = width1;
                    continue;
                }
                var color1 = ReadCssColor();
                if (color1 == null)
                {
                    all++;
                    Next(pos - Position);//返回读前的位置  
                }
                else
                {
                    color = color1;
                    continue;
                }
                if (all == 3)//三组值均不能正常读取
                {
                    throw new CssSyntaxException(Position, 1, "无法正确解析border属性值的数据。");
                }
            }
            return new CssBorder() { Color = color, Style = style, Width = width, Orientation = CssOrientation.All };
        }

        #endregion

        #region Align
        /// <summary>
        /// 获取字符的对齐方式
        /// </summary>
        /// <returns></returns>
        public CssTextAlign? ReadTextAlign()
        {
            SkipWhiteSpace();
            string str = ReadIdentifier();
            switch (str)
            {
                case "left":
                    return CssTextAlign.Left;
                case "right":
                    return CssTextAlign.Right;
                case "center":
                    return CssTextAlign.Center;
                case "justify":
                    return CssTextAlign.Justify;
                default:
                    return null;
            }
        }
        #endregion

        #region Statics/实例方法的静态简便调用
        public static CssBorder ReadBoder(string source)
        {
            CssValueLexer l = new CssValueLexer(source);
            return l.ReadBorder();
        }

        public static CssBorderStyle[] ReadBorderStyle(string source)
        {
            CssValueLexer l = new CssValueLexer(source);
            return l.ReadBorderStyles();
        }

        public static CssLength[] ReadMargins(string source)
        {
            CssValueLexer l = new CssValueLexer(source);
            return l.ReadCssLengths(); 
        }

        public static int[] ReadCssColors(string source)
        {
            CssValueLexer l = new CssValueLexer(source);
            return l.ReadCssColors();
        }
         
        public static CssTextAlign? ReadTextAlign(string source)
        {
            CssValueLexer l = new CssValueLexer(source);
            return l.ReadTextAlign();
        }
        #endregion
    }
}
